// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package walk

import (
	"go/constant"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// walkAssign遍历OAS（AssignExpr）或OASOP（AssignOpExpr）节点。
func walkAssign(init *ir.Nodes, n ir.Node) ir.Node {
	init.Append(ir.TakeInit(n)...)

	var left, right ir.Node
	switch n.Op() {
	case ir.OAS:
		n := n.(*ir.AssignStmt)
		left, right = n.X, n.Y
	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		left, right = n.X, n.Y
	}

	// 识别m[k]=append（m[k]，…），以便我们可以重用
	// mapassign调用。
	var mapAppend *ir.CallExpr
	if left.Op() == ir.OINDEXMAP && right.Op() == ir.OAPPEND {
		left := left.(*ir.IndexExpr)
		mapAppend = right.(*ir.CallExpr)
		if !ir.SameSafeExpr(left, mapAppend.Args[0]) {
			base.Fatalf("not same expressions: %v != %v", left, mapAppend.Args[0])
		}
	}

	left = walkExpr(left, init)
	left = safeExpr(left, init)
	if mapAppend != nil {
		mapAppend.Args[0] = left
	}

	if n.Op() == ir.OASOP {
		// 将x op=y重写为x=x op y。
		n = ir.NewAssignStmt(base.Pos, left, typecheck.Expr(ir.NewBinaryExpr(base.Pos, n.(*ir.AssignOpStmt).AsOp, left, right)))
	} else {
		n.(*ir.AssignStmt).X = left
	}
	as := n.(*ir.AssignStmt)

	if oaslit(as, init) {
		return ir.NewBlockStmt(as.Pos(), nil)
	}

	if as.Y == nil {
		// TODO（奥斯汀）：检查所有“隐式归零”
		return as
	}

	if !base.Flag.Cfg.Instrumenting && ir.IsZero(as.Y) {
		return as
	}

	switch as.Y.Op() {
	default:
		as.Y = walkExpr(as.Y, init)

	case ir.ORECV:
		// x=<-c；左是x，右是c。
		// order.stmt确保x是可寻址的。
		recv := as.Y.(*ir.UnaryExpr)
		recv.X = walkExpr(recv.X, init)

		n1 := typecheck.NodAddr(as.X)
		r := recv.X // 海峡
		return mkcall1(chanfn("chanrecv1", 2, r.Type()), nil, init, r, n1)

	case ir.OAPPEND:
		// x=附加（…）
		call := as.Y.(*ir.CallExpr)
		if call.Type().Elem().NotInHeap() {
			base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", call.Type().Elem())
		}
		var r ir.Node
		switch {
		case isAppendOfMake(call):
			// x=附加（y，make（[]T，y）…）
			r = extendSlice(call, init)
		case call.IsDDD:
			r = appendSlice(call, init) // 也适用于追加（切片、字符串）。
		default:
			r = walkAppend(call, init, as)
		}
		as.Y = r
		if r.Op() == ir.OAPPEND {
			// 为后端保留原位。
			// 不要添加新的写屏障。
			// 为后端设置类型的地址。
			r.(*ir.CallExpr).X = reflectdata.TypePtr(r.Type().Elem())
			return as
		}
		// 否则，为种族检测器降低。
		// 视为普通作业。
	}

	if as.X != nil && as.Y != nil {
		return convas(as, init)
	}
	return as
}

// walkAssignDotType遍历OAS2DOTTYPE节点。
func walkAssignDotType(n *ir.AssignListStmt, init *ir.Nodes) ir.Node {
	walkExprListSafe(n.Lhs, init)
	n.Rhs[0] = walkExpr(n.Rhs[0], init)
	return n
}

// walkAssignFunc遍历OAS2FUNC节点。
func walkAssignFunc(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
	init.Append(ir.TakeInit(n)...)

	r := n.Rhs[0]
	walkExprListSafe(n.Lhs, init)
	r = walkExpr(r, init)

	if ir.IsIntrinsicCall(r.(*ir.CallExpr)) {
		n.Rhs = []ir.Node{r}
		return n
	}
	init.Append(r)

	ll := ascompatet(n.Lhs, r.Type())
	return ir.NewBlockStmt(src.NoXPos, ll)
}

// walkAssignList遍历OAS2节点。
func walkAssignList(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
	init.Append(ir.TakeInit(n)...)
	return ir.NewBlockStmt(src.NoXPos, ascompatee(ir.OAS, n.Lhs, n.Rhs))
}

// walkAssignMapRead遍历OAS2MAPR节点。
func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
	init.Append(ir.TakeInit(n)...)

	r := n.Rhs[0].(*ir.IndexExpr)
	walkExprListSafe(n.Lhs, init)
	r.X = walkExpr(r.X, init)
	r.Index = walkExpr(r.Index, init)
	t := r.X.Type()

	fast := mapfast(t)
	key := mapKeyArg(fast, r, r.Index)

	// 发件人：
	// a、 b=m[i]
	// 致：
	// var，b=mapaccess2*（t，m，i）
	// a=*var
	a := n.Lhs[0]

	var call *ir.CallExpr
	if w := t.Elem().Width; w <= zeroValSize {
		fn := mapfn(mapaccess2[fast], t, false)
		call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key)
	} else {
		fn := mapfn("mapaccess2_fat", t, true)
		z := reflectdata.ZeroAddr(w)
		call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key, z)
	}

	// mapaccess2*返回类型化bool，但由于规范更改，
	// i.（T）的布尔结果现在是非类型化的，因此我们将其作为
	// 与lhs上的变量类型相同。
	if ok := n.Lhs[1]; !ir.IsBlank(ok) && ok.Type().IsBoolean() {
		call.Type().Field(1).Type = ok.Type()
	}
	n.Rhs = []ir.Node{call}
	n.SetOp(ir.OAS2FUNC)

	// 如果是，则不生成=*var_
	if ir.IsBlank(a) {
		return walkExpr(typecheck.Stmt(n), init)
	}

	var_ := typecheck.Temp(types.NewPtr(t.Elem()))
	var_.SetTypecheck(1)
	var_.MarkNonNil() // mapaccess始终返回非nil指针

	n.Lhs[0] = var_
	init.Append(walkExpr(n, init))

	as := ir.NewAssignStmt(base.Pos, a, ir.NewStarExpr(base.Pos, var_))
	return walkExpr(typecheck.Stmt(as), init)
}

// walkAssignRecv遍历OAS2RECV节点。
func walkAssignRecv(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
	init.Append(ir.TakeInit(n)...)

	r := n.Rhs[0].(*ir.UnaryExpr) // 记录
	walkExprListSafe(n.Lhs, init)
	r.X = walkExpr(r.X, init)
	var n1 ir.Node
	if ir.IsBlank(n.Lhs[0]) {
		n1 = typecheck.NodNil()
	} else {
		n1 = typecheck.NodAddr(n.Lhs[0])
	}
	fn := chanfn("chanrecv2", 2, r.X.Type())
	ok := n.Lhs[1]
	call := mkcall1(fn, types.Types[types.TBOOL], init, r.X, n1)
	return typecheck.Stmt(ir.NewAssignStmt(base.Pos, ok, call))
}

// walkReturn遍历ORETURN节点。
func walkReturn(n *ir.ReturnStmt) ir.Node {
	fn := ir.CurFunc

	fn.NumReturns++
	if len(n.Results) == 0 {
		return n
	}

	results := fn.Type().Results().FieldSlice()
	dsts := make([]ir.Node, len(results))
	for i, v := range results {
		// TODO（mdempsky）：typecheck应该已经检查了结果变量。
		dsts[i] = typecheck.AssignExpr(v.Nname.(*ir.Name))
	}

	n.Results = ascompatee(n.Op(), dsts, n.Results)
	return n
}

// 选中“将类型列表分配给”
// 表达式列表。叫来
// expr list=func（）
func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node {
	if len(nl) != nr.NumFields() {
		base.Fatalf("ascompatet: assignment count mismatch: %d = %d", len(nl), nr.NumFields())
	}

	var nn ir.Nodes
	for i, l := range nl {
		if ir.IsBlank(l) {
			continue
		}
		r := nr.Field(i)

		// 订单应已为其创建适当类型的自动temp
		// 我们需要将结果存储到。
		if tmp, ok := l.(*ir.Name); !ok || !tmp.AutoTemp() || !types.Identical(tmp.Type(), r.Type) {
			base.FatalfAt(l.Pos(), "assigning %v to %+v", r.Type, l)
		}

		res := ir.NewResultExpr(base.Pos, nil, types.BADWIDTH)
		res.Index = int64(i)
		res.SetType(r.Type)
		res.SetTypecheck(1)

		nn.Append(ir.NewAssignStmt(base.Pos, l, res))
	}
	return nn
}

// 选中“将表达式列表指定给”
// 表达式列表。叫来
// expr list=expr list
func ascompatee(op ir.Op, nl, nr []ir.Node) []ir.Node {
	// 无法发生：应在类型检查期间被拒绝
	if len(nl) != len(nr) {
		base.Fatalf("assignment operands mismatch: %+v / %+v", ir.Nodes(nl), ir.Nodes(nr))
	}

	var assigned ir.NameSet
	var memWrite, deferResultWrite bool

	// 受影响报告表达式n是否受
	// 到目前为止，这些作业都适用。
	affected := func(n ir.Node) bool {
		if deferResultWrite {
			return true
		}
		return ir.Any(n, func(n ir.Node) bool {
			if n.Op() == ir.ONAME && assigned.Has(n.(*ir.Name)) {
				return true
			}
			if memWrite && readsMemory(n) {
				return true
			}
			return false
		})
	}

	// 如果所需的表达式可能受
	// 早一点的作业，早一点复印
	// 表达式，并改为使用副本。
	var early ir.Nodes
	save := func(np *ir.Node) {
		if n := *np; affected(n) {
			*np = copyExpr(n, n.Type(), &early)
		}
	}

	var late ir.Nodes
	for i, lorig := range nl {
		l, r := lorig, nr[i]

		// 返回时不要生成“x=x”。见第4014期。
		if op == ir.ORETURN && ir.SameSafeExpr(l, r) {
			continue
		}

		// 保存左侧所需的子表达式。
		// 钻取非解引用。
		for {
			// 如果表达式有init语句，则必须对其求值
			// 在其任何保存的子操作数（#45706）之前。
			// TODO（mdempsky）：不允许对左值使用init语句。
			init := ir.TakeInit(l)
			walkStmtList(init)
			early.Append(init...)

			switch ll := l.(type) {
			case *ir.IndexExpr:
				if ll.X.Type().IsArray() {
					save(&ll.Index)
					l = ll.X
					continue
				}
			case *ir.ParenExpr:
				l = ll.X
				continue
			case *ir.SelectorExpr:
				if ll.Op() == ir.ODOT {
					l = ll.X
					continue
				}
			}
			break
		}

		var name *ir.Name
		switch l.Op() {
		default:
			base.Fatalf("unexpected lvalue %v", l.Op())
		case ir.ONAME:
			name = l.(*ir.Name)
		case ir.OINDEX, ir.OINDEXMAP:
			l := l.(*ir.IndexExpr)
			save(&l.X)
			save(&l.Index)
		case ir.ODEREF:
			l := l.(*ir.StarExpr)
			save(&l.X)
		case ir.ODOTPTR:
			l := l.(*ir.SelectorExpr)
			save(&l.X)
		}

		// 在右侧保存表达式。
		save(&r)

		appendWalkStmt(&late, convas(ir.NewAssignStmt(base.Pos, lorig, r), &late))

		// 检查可能需要计算后续表达式的原因
		// 在这个任务发生之前。

		if name == nil {
			// 不是对声明变量的直接赋值。
			// 保守地假设任何内存访问都可能是别名。
			memWrite = true
			continue
		}

		if name.Class == ir.PPARAMOUT && ir.CurFunc.HasDefer() {
			// 对具有延迟的函数中的结果参数的赋值
			// 如果对任何后续表达式进行求值，则早期可见
			// 恐慌（43835）。
			deferResultWrite = true
			continue
		}

		if sym := types.OrigSym(name.Sym()); sym == nil || sym.IsBlank() {
			// 我们可以忽略对空白或匿名结果参数的赋值。
			// 无论如何，这些都不能出现在表达式中。
			continue
		}

		if name.Addrtaken() || !name.OnStack() {
			// 全局变量、堆转义或只是addrtaken。
			// 保守地假设任何内存访问都可能是别名。
			memWrite = true
			continue
		}

		// 本地、非addrtaken变量。
		// 分配只能直接使用此变量。
		assigned.Add(name)
	}

	early.Append(late.Take()...)
	return early
}

// readsMemory报告求值n是否直接从
// 可能被间接写入的内存。
func readsMemory(n ir.Node) bool {
	switch n.Op() {
	case ir.ONAME:
		n := n.(*ir.Name)
		if n.Class == ir.PFUNC {
			return false
		}
		return n.Addrtaken() || !n.OnStack()

	case ir.OADD,
		ir.OAND,
		ir.OANDAND,
		ir.OANDNOT,
		ir.OBITNOT,
		ir.OCONV,
		ir.OCONVIFACE,
		ir.OCONVNOP,
		ir.ODIV,
		ir.ODOT,
		ir.ODOTTYPE,
		ir.OLITERAL,
		ir.OLSH,
		ir.OMOD,
		ir.OMUL,
		ir.ONEG,
		ir.ONIL,
		ir.OOR,
		ir.OOROR,
		ir.OPAREN,
		ir.OPLUS,
		ir.ORSH,
		ir.OSUB,
		ir.OXOR:
		return false
	}

	// 要保守。
	return true
}

// 展开附加（l1，l2…）到
// 初始化{
// s:=l1
// n:=len（s）+len（l2）
// None
// 如果uint（n）>uint（cap（s））{
// s=生长切片（s，n）
// }
// s=s[：n]
// memmove（&s[len（l1）]，&l2[0]，len（l2）*sizeof（T））
// }
// s
// None
// l2允许是字符串。
func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
	walkAppendArgs(n, init)

	l1 := n.Args[0]
	l2 := n.Args[1]
	l2 = cheapExpr(l2, init)
	n.Args[1] = l2

	var nodes ir.Nodes

	// var s[]T
	s := typecheck.Temp(l1.Type())
	nodes.Append(ir.NewAssignStmt(base.Pos, s, l1)) // s=l1

	elemtype := s.Type().Elem()

	// n:=len（s）+len（l2）
	nn := typecheck.Temp(types.Types[types.TINT])
	nodes.Append(ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), ir.NewUnaryExpr(base.Pos, ir.OLEN, l2))))

	// 如果uint（n）>uint（cap（s））
	nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
	nuint := typecheck.Conv(nn, types.Types[types.TUINT])
	scapuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT])
	nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, scapuint)

	// 实例化GrowtSlice（典型*type，[]any，int）[]any
	fn := typecheck.LookupRuntime("growslice")
	fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)

	// s=生长切片（T，s，n）
	nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
	nodes.Append(nif)

	// s=s[：n]
	nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil)
	nt.SetBounded(true)
	nodes.Append(ir.NewAssignStmt(base.Pos, s, nt))

	var ncopy ir.Node
	if elemtype.HasPointers() {
		// 副本（s[len（l1）：]，l2）
		slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil)
		slice.SetType(s.Type())

		ir.CurFunc.SetWBPos(n.Pos())

		// 实例化typedslicecopy（typ*type，dstPtr*any，dstLen int，srcPtr*any，srcLen int）int
		fn := typecheck.LookupRuntime("typedslicecopy")
		fn = typecheck.SubstArgTypes(fn, l1.Type().Elem(), l2.Type().Elem())
		ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes))
		ptr2, len2 := backingArrayPtrLen(l2)
		ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.TypePtr(elemtype), ptr1, len1, ptr2, len2)
	} else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime {
		// 依赖于运行时来检测：
		// 副本（s[len（l1）：]，l2）
		// l2可以是片或字符串。
		slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil)
		slice.SetType(s.Type())

		ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes))
		ptr2, len2 := backingArrayPtrLen(l2)

		fn := typecheck.LookupRuntime("slicecopy")
		fn = typecheck.SubstArgTypes(fn, ptr1.Type().Elem(), ptr2.Type().Elem())
		ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Width))
	} else {
		// memmove（&s[len（l1）]，&l2[0]，len（l2）*sizeof（T））
		ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1))
		ix.SetBounded(true)
		addr := typecheck.NodAddr(ix)

		sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l2)

		nwid := cheapExpr(typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, l2), types.Types[types.TUINTPTR]), &nodes)
		nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Width))

		// 实例化func memmove（to*any，frm*any，length uintpttr）
		fn := typecheck.LookupRuntime("memmove")
		fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
		ncopy = mkcall1(fn, nil, &nodes, addr, sptr, nwid)
	}
	ln := append(nodes, ncopy)

	typecheck.Stmts(ln)
	walkStmtList(ln)
	init.Append(ln...)
	return s
}

// isAppendOfMake报告n是否为append（x，make（[]T，y）…）形式。
// isAppendOfMake假设n已经过类型检查。
func isAppendOfMake(n ir.Node) bool {
	if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
		return false
	}

	if n.Typecheck() == 0 {
		base.Fatalf("missing typecheck: %+v", n)
	}

	if n.Op() != ir.OAPPEND {
		return false
	}
	call := n.(*ir.CallExpr)
	if !call.IsDDD || len(call.Args) != 2 || call.Args[1].Op() != ir.OMAKESLICE {
		return false
	}

	mk := call.Args[1].(*ir.MakeExpr)
	if mk.Cap != nil {
		return false
	}

	// y必须是整型常量或可能的最大正值
	// 变量y的值需要适合uint。

	// typecheck确保要生成的常量参数不是负数，并且适合整数。

	// 在运行时，通过显式检查int（len）<0来处理要生成的len参数的溢出。
	y := mk.Len
	if !ir.IsConst(y, constant.Int) && y.Type().Size() > types.Types[types.TUINT].Size() {
		return false
	}

	return true
}

// extendSlice重写将（l1，make（[]T，l2）…）追加到
// 初始化{
// 如果l2>=0{
// }否则{
// panicmakeslicelen（）
// }
// s:=l1
// n:=len（s）+l2
// None
// None
// None
// None
// None
// 如果uint（n）>uint（cap（s））{
// s=生长切片（T，s，n）
// }
// s=s[：n]
// lptr:=&l1[0]
// sptr:=&s[0]
// 如果lptr==sptr | |！T.HasPointers（）{
// None
// hp:=&s[len（l1）]
// hn:=l2*sizeof（T）
// memclr（hp，hn）
// }
// }
// s
func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
	// isAppendOfMake确保l2的所有可能正值都适合uint。
	// 当从uint转换为int时，l2溢出的情况由显式
	// 在下面生成的运行时检查l2<0。
	l2 := typecheck.Conv(n.Args[1].(*ir.MakeExpr).Len, types.Types[types.TINT])
	l2 = typecheck.Expr(l2)
	n.Args[1] = l2 // walkAppendArgs需要n.List.Second（）中的l2。

	walkAppendArgs(n, init)

	l1 := n.Args[0]
	l2 = n.Args[1] // 重新读取l2，因为它可能已由walkAppendArgs更新

	var nodes []ir.Node

	// 如果l2>=0（可能发生），则不执行任何操作
	nifneg := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGE, l2, ir.NewInt(0)), nil, nil)
	nifneg.Likely = true

	// 否则，请参阅slicelen（）
	nifneg.Else = []ir.Node{mkcall("panicmakeslicelen", nil, init)}
	nodes = append(nodes, nifneg)

	// s:=l1
	s := typecheck.Temp(l1.Type())
	nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, l1))

	elemtype := s.Type().Elem()

	// n:=len（s）+l2
	nn := typecheck.Temp(types.Types[types.TINT])
	nodes = append(nodes, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2)))

	// 如果uint（n）>uint（cap（s））
	nuint := typecheck.Conv(nn, types.Types[types.TUINT])
	capuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT])
	nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, capuint), nil, nil)

	// 实例化GrowtSlice（典型*类型，旧[]任意，新CAP int）[]任意
	fn := typecheck.LookupRuntime("growslice")
	fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)

	// s=生长切片（T，s，n）
	nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
	nodes = append(nodes, nif)

	// s=s[：n]
	nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil)
	nt.SetBounded(true)
	nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, nt))

	// lptr:=&l1[0]
	l1ptr := typecheck.Temp(l1.Type().Elem().PtrTo())
	tmp := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l1)
	nodes = append(nodes, ir.NewAssignStmt(base.Pos, l1ptr, tmp))

	// sptr:=&s[0]
	sptr := typecheck.Temp(elemtype.PtrTo())
	tmp = ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
	nodes = append(nodes, ir.NewAssignStmt(base.Pos, sptr, tmp))

	// hp:=&s[len（l1）]
	ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1))
	ix.SetBounded(true)
	hp := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])

	// hn:=l2*sizeof（元素）
	hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Width)), types.Types[types.TUINTPTR])

	clrname := "memclrNoHeapPointers"
	hasPointers := elemtype.HasPointers()
	if hasPointers {
		clrname = "memclrHasPointers"
		ir.CurFunc.SetWBPos(n.Pos())
	}

	var clr ir.Nodes
	clrfn := mkcall(clrname, nil, &clr, hp, hn)
	clr.Append(clrfn)

	if hasPointers {
		// 如果l1ptr==sptr
		nifclr := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OEQ, l1ptr, sptr), nil, nil)
		nifclr.Body = clr
		nodes = append(nodes, nifclr)
	} else {
		nodes = append(nodes, clr...)
	}

	typecheck.Stmts(nodes)
	walkStmtList(nodes)
	init.Append(nodes...)
	return s
}
